//-------------------------------------------------------------------------------------------------
//  line.hpp - Definition of line class (of infinite length) for physics operations
//  (c) 2009 Simon Miessler
//-------------------------------------------------------------------------------------------------

#pragma once

#include <boost/optional.hpp>

#include "math_exports.hpp"
#include "geometry_fwd.hpp"
#include "intersectable.hpp"
#include "matrix4x4.hpp"




namespace math
{
	/**
	 * Line represents a line in 3d-space of infinite length.
	 */
	template <typename T>
	class line
		: public intersectable< line<T> >
	{
	public:

		/// The used floating-point type
		typedef typename T   float_t;

	public:

		/// The position of the line
		vector3<T>   position;

		/// The direction the line points to
		vector3<T>   direction;

	public:

		/**
		 * Create a line.
		 * It is located at (0,0,0) and points to (0,1,0)
		 */
		line()
			: position(0, 0, 0)
			, direction(0, 1, 0)
		{}

		/**
		 * Create a line at the specified position with the specified direction.
		 */
		line(const vector3<T>& position, const vector3<T>& direction)
			: position(position)
			, direction(direction)
		{}



		/**
		 * Get a point along the line.
		 *
		 * @param t   the distance to the base of the ray
		 */
		vector3<T> point(T t) const
		{
			return position + t * direction;
		}



		/**
		 * Calculate the closest ray between this line and the other one.
		 * Always returns a valid result, unless both lines are parallel.
		 *
		 * This function returns the parameters for this line (pair.first)
		 * and for the other one (pair.second).
		 */
		boost::optional< std::pair<T,T> > closestRayT(const line<T>& l) const
		{
			vector3<T> p13 = position - l.position;
			vector3<T> p43 = l.direction;
			vector3<T> p21 = direction;

			T d1343 = p13.x * p43.x + p13.y * p43.y + p13.z * p43.z;
			T d4321 = p43.x * p21.x + p43.y * p21.y + p43.z * p21.z;
			T d1321 = p13.x * p21.x + p13.y * p21.y + p13.z * p21.z;
			T d4343 = p43.x * p43.x + p43.y * p43.y + p43.z * p43.z;
			T d2121 = p21.x * p21.x + p21.y * p21.y + p21.z * p21.z;

			T d = d2121 * d4343 - d4321 * d4321;
			if(d == T(0))
				return boost::optional< std::pair<T,T> >();

			T n = d1343 * d4321 - d1321 * d4343;

			T u = n/d;
			T v = (d1343 + d4321 * u) / d4343;

			return std::pair<T,T>(u, v);
		}
		///////////////////////////////////////////////////////////////////////////////////////////

		/**
		 * Calculate the closest ray between this line and the other one.
		 * Always returns a valid result, unless both lines are parallel.
		 *
		 * This function returns a ray that starts on this line, and ends
		 * on the other one.
		 */
		boost::optional< ray<T> > closestRay(const line<T>& l) const
		{
			boost::optional< std::pair<T,T> > p = closestRayT(l);
			return (p) ? ray<T>(point(p->first), l.point(p->second)) : boost::optional< ray<T> >();
		}
		///////////////////////////////////////////////////////////////////////////////////////////

		/**
		 * Calculate the closest ray between this line and a ray.
		 * This function actually returns the parameters
		 */
		boost::optional< std::pair<T,T> > closestRayT(const ray<T>& r) const
		{
			boost::optional< std::pair<T,T> > p = closestRayT(line<T>(r.start, r.direction));
			return (p && r.isValid(p->second)) ? p : boost::optional< std::pair<T,T> >();
		}
		///////////////////////////////////////////////////////////////////////////////////////////

		/**
		 * Calculate the closest ray between this line and a ray.
		 */
		boost::optional< ray<T> > closestRay(const ray<T>& r) const
		{
			// Create a ray that starts on our line and ends on the other ray.
			boost::optional< std::pair<T,T> > p = closestRayT(r);
			return (p) ? ray<T>(point(p->first), r.point(p->second)) : boost::optional< ray<T> >();
		}
		///////////////////////////////////////////////////////////////////////////////////////////
	};
	///////////////////////////////////////////////////////////////////////////////////////////////




	/**
	 * Transform a Line by a 4x4 matrix.
	 *
	 * @return   the transformed line
	 */
	template <typename T>
	line<T> transform(const matrix4x4<T>& lhs, const line<T>& rhs)
	{
		return line<T>(transform(lhs, rhs.position), transform(lhs, rhs.direction));
	}
	///////////////////////////////////////////////////////////////////////////////////////////////




	/**
	 * In order to compile a binary that actually contains the code for our Lines,
	 * we have to explicitly instantiate the template Lines and export those:
	 */
	template struct MATH_API intersectable< line<float> >;
	template struct MATH_API intersectable< line<double> >;
	template class MATH_API line<float>;
	template class MATH_API line<double>;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
